package cz.najman.emdroid.service;

import java.util.Date;

import android.app.IntentService;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.util.Log;
import cz.najman.emdroid.connection.ConnectionMethod;
import cz.najman.emdroid.connection.Proccesor;
import cz.najman.emdroid.connection.SimpleProccesor;
import cz.najman.emdroid.content.ContentHandler;
import cz.najman.emdroid.content.ContentProducer;
import cz.najman.emdroid.content.SmartContentHandler;
import cz.najman.emdroid.content.SmartContentProducer;
import cz.najman.emdroid.mappings.Mappings;
import cz.najman.emdroid.utils.RequestIdentifier;

/**
 * @author Lukáš Najman
 */
public class UpdaterService extends IntentService {

	private Proccesor mRemoteExecutor;
	private Date mDate;

	public UpdaterService() {
		super(TAG);
	}

	public UpdaterService(String name) {
		super(name);
	}

	@Override
	public void onCreate() {
		super.onCreate();
		final Context context = getApplication().getApplicationContext();
		mRemoteExecutor = new SimpleProccesor(getContentResolver(),
				context);
		mDate = new Date();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Log.d(TAG, "OnHandleIntent");

		
			// Extract the extra from intent and add them to the RequestIdentifier
			RequestIdentifier identifier = new RequestIdentifier(mDate.getTime());

			identifier.setMethod(intent.getIntExtra(METHOD, ConnectionMethod.METHOD_NONE));
			identifier.setUri((Uri) intent.getParcelableExtra(URI) );
			identifier.setParameterTypes(intent.getStringArrayExtra(PARAMETER_TYPES));
			identifier.setParameterValues(intent.getStringArrayExtra(PARAMETER_VALUES));

			// Extract the ResultReceiver to be use to send message back to Helper
			// class about the pending operation
			ResultReceiver receiver = intent.getParcelableExtra(RESULT_RECEIVER);

			Class object = (Class) intent.getSerializableExtra(OBJECT);

			// Fill the bundle object with the STATUS_CODE and objects Class of the
			// pending operation
			Bundle bundle = new Bundle();
			bundle.putSerializable(OBJECT, object);
			receiver.send(STATUS_RUNNING, bundle);
			try {
			int responseCode = -1;

			if ((identifier.getMethod() != ConnectionMethod.METHOD_NONE) && (object != null)) {

				ContentValues values = intent.getParcelableExtra(CONTENT_VALUES);
				responseCode = executeUpdate(object, identifier, values);
			}

			if ((responseCode >= ConnectionMethod.RESPONSE_CODE_OK) && (responseCode < 300)) {
				receiver.send(STATUS_FINISHED, bundle);
			} else {
				receiver.send(STATUS_ERROR, bundle);
			}
		} catch (Exception e) {
			if(e.getMessage() == null) Log.d(TAG, "Unknown exception");
			else Log.e(TAG, e.getMessage());
			receiver.send(STATUS_ERROR, bundle);
		}
		

	}

	/**
	 * @param object
	 *            Id of the object on which to execute the update
	 * @param identifier
	 *            RequestIdentifier instance, containing the informations needed
	 *            to identify concrete pending request
	 * @param values
	 *            ContentValues with data
	 * @return Int responseCode holds the conventions of the HTTP response
	 *         codes. Returns -1 if an error occurred.
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 */
	private int executeUpdate(Class object, RequestIdentifier identifier, ContentValues values) throws IllegalAccessException, InstantiationException {

		final int method = identifier.getMethod();
		ContentHandler contentHandler = null;
		ContentProducer contentProducer = null;

		if (method != ConnectionMethod.METHOD_DELETE) {
			contentHandler = getContentHandler(object);
		}

		if ((method == ConnectionMethod.METHOD_POST) || (method == ConnectionMethod.METHOD_UPDATE)) {
			contentProducer = getContentProducer(object);
			contentProducer.setData(values);			
		}

		int responseCode = -1;		
			switch (method){
			case ConnectionMethod.METHOD_GET:
				responseCode = mRemoteExecutor.retrieve(identifier, contentHandler);
				break;
			case ConnectionMethod.METHOD_POST:
				responseCode = mRemoteExecutor.create(identifier, contentHandler, contentProducer);
				break;	
			case ConnectionMethod.METHOD_UPDATE:
				responseCode = mRemoteExecutor.update(identifier, contentHandler, contentProducer);
				break;
			case ConnectionMethod.METHOD_DELETE:
				responseCode = mRemoteExecutor.delete(identifier);
				break;	
			}		
		return responseCode;

	}

	private ContentProducer getContentProducer(Class objectClass) throws IllegalAccessException, InstantiationException {		
			
		Mappings mappingObject = (Mappings) objectClass.newInstance();
		return new SmartContentProducer(mappingObject);				
	}

	private ContentHandler getContentHandler(Class objectClass) throws IllegalAccessException, InstantiationException {
		
		Mappings mappingObject = (Mappings) objectClass.newInstance();
		return new SmartContentHandler(mappingObject);
			
	}

	private static final String TAG = "Updater service";

	public static final String RESULT_RECEIVER = "result_receiver";
	public static final String METHOD = "method";
	public static final String CONTENT_VALUES = "content_values";
	public static final String URI = "uri";
	public static final String PARAMETER_TYPES = "parameter_types";
	public static final String PARAMETER_VALUES = "parameter_values";

	public static final String OBJECT = "object";

	public static final int STATUS_RUNNING = 1;
	public static final int STATUS_FINISHED = 2;
	public static final int STATUS_ERROR = 3;

}
